package game.role;

import engine.input.Key;
import game.controller.Entity;
import game.controller.Projectile;
import game.controller.Supplies;
import game.goods.OBJ_Fireball;
import game.goods.Obj_Key;
import game.goods.Obj_Shield_Wood;
import game.goods.Obj_Sword_Normal;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.shape.Rectangle;

import java.util.ArrayList;
import java.util.HashMap;

import static engine.app.View.keyH;
import static game.basic.GameBase.*;
import static game.basic.Util.getImage;
import static game.main.GameApp.gameMusic;
import static game.main.GamePanel.gameState;
import static game.main.GamePanel.ui;

public class Player extends Entity {

    public HashMap<Integer, Image[]> spriteMap;

    int stopCounter;//恢复第一帧计数器 默认为0

    int tempScreenX;
    int tempScreenY;

    Rectangle attackArea;//攻击区域

    public Supplies currentWeapon;
    public Supplies currentShield;

    Projectile projectile;//发射器

    public ArrayList<Supplies> inventoryBag = new ArrayList<>();

    public int maxBagSize = 20;

    public int maxMana;
    public int mana;

    public Player() {
        this.spriteMap = new HashMap<>();
        readSpriteImage("/boy/walk/boy_%s_%d.png", tileSize, 0, spriteMap);
        readAttackImage("/boy/attack/boy_", "attack", "_%s_%d.png", tileSize, tileSize);

        setDefaultValues();
        setBagItems();
    }

    public void setDefaultValues() {
        worldX = tileSize * 23;
        worldY = tileSize * 21;
        speed = 4;
        direction = downDir;

        solidAreaX = 8;
        solidAreaY = 16;
        solidAreaWidth = 32;
        solidAreaHeight = 32;

        level = 1;
        maxHp = 6;
        hp = maxHp;
        strength = 1;
        dexterity = 1;
        exp = 0;
        nextLevelExp = 5;
        coin = 100;

        currentWeapon = new Obj_Sword_Normal();//武器
        currentShield = new Obj_Shield_Wood();//防具
        projectile = new OBJ_Fireball();//发射器

        attackArea = new Rectangle();


        attack = getAttack();
        defense = getDefense();
    }

    public void setBagItems() {
        inventoryBag.clear();
        inventoryBag.add(currentWeapon);//武器
        inventoryBag.add(currentShield);//防具
        inventoryBag.add(new Obj_Key());
    }

    private int getAttack() {
        attackArea.setWidth(currentWeapon.solidAreaWidth);
        attackArea.setHeight(currentWeapon.solidAreaHeight);
        return strength * currentWeapon.attackValue;
    }

    private int getDefense() {
        return dexterity + currentShield.defenseValue;
    }


    public void readAttackImage(String path, String type, String end, int width, int height) {
        String url = path + type + end;

        Image[] up = new Image[ImgFps];
        Image[] down = new Image[ImgFps];
        Image[] left = new Image[ImgFps];
        Image[] right = new Image[ImgFps];

        String[] directions = {"up", "down", "left", "right"};
        int[] frames = {0, 1};

        for (String d : directions) {
            for (int frame : frames) {
                String imagePath = String.format(url, d, frame + 1);
                switch (d) {
                    case "up" -> up[frame] = getImage(imagePath, width, 0);
                    case "down" -> down[frame] = getImage(imagePath, width, 0);
                    case "left" -> left[frame] = getImage(imagePath, 0, height);
                    case "right" -> right[frame] = getImage(imagePath, 0, height);
                }
            }
        }

        spriteMap.put(upAttack, up);
        spriteMap.put(downAttack, down);
        spriteMap.put(leftAttack, left);
        spriteMap.put(rightAttack, right);
    }

    /**
     * @param gc 画笔
     *           绘制攻击区域
     *           gc.fillRect(screenX + solidAreaX+(solidAreaWidth-attackArea.getWidth())/2, screenY + solidAreaY-attackArea.getHeight(), attackArea.getWidth(), attackArea.getHeight());//上
     *           gc.fillRect(screenX + solidAreaX-attackArea.getHeight(), screenY + solidAreaY+(solidAreaWidth-attackArea.getWidth())/2, attackArea.getHeight(), attackArea.getWidth());//左
     *           gc.fillRect(screenX + solidAreaX+(solidAreaWidth-attackArea.getWidth())/2, screenY + solidAreaY+solidAreaHeight, attackArea.getWidth(), attackArea.getHeight());//下
     *           gc.fillRect(screenX + solidAreaX+solidAreaWidth, screenY + solidAreaY+(solidAreaWidth-attackArea.getWidth())/2, attackArea.getHeight(), attackArea.getWidth());//右
     */
    @Override
    public void draw(GraphicsContext gc) {
        tempScreenX = screenX;
        tempScreenY = screenY;
        if (isAttacking) {
            switch (direction) {
                case upAttack -> tempScreenY -= tileSize;
                case leftAttack -> tempScreenX -= tileSize;
            }
        }
        if (invincible) {
            gc.setGlobalAlpha(0.5);
            speed = 2;
            if (++invincibleCounter >= 120) {
                invincible = false;
                invincibleCounter = 0;
                speed = 4;
            }
        }
        gc.drawImage(spriteMap.get(direction)[spriteNum], tempScreenX, tempScreenY);
        gc.setGlobalAlpha(1);
        projectile.draw(gc);
    }

    public void update() {
        if (isAttacking) {
            attacking();
        } //没有进行攻击
        else {
            if (isMoveKey()) {
                switch (keyH.isKey) {
                    case UP -> direction = upDir;
                    case DOWN -> direction = downDir;
                    case LEFT -> direction = leftDir;
                    case RIGHT -> direction = rightDir;
                }

                collisionOn = false;
                cChecker.checkTile(this, currentMap);
                pickUpObject(cChecker.checkObject(this, true));
                cChecker.checkEntity(this, npc);
                contactMonster(cChecker.checkEntity(this, monster));
                eHandler.checkEvent();

                if (!collisionOn) {
                    switch (direction) {
                        case upDir -> worldY -= speed;
                        case downDir -> worldY += speed;
                        case leftDir -> worldX -= speed;
                        case rightDir -> worldX += speed;
                    }
                }

                if (++spriteCounter % 12 == 0) { // 每10帧改变一次 一秒5帧
                    spriteNum = (spriteNum + 1) % ImgFps;
                    spriteCounter = 0;
                }
            } //
            else {
                if (++stopCounter == 30) {
                    spriteNum = 0;
                    stopCounter = 0;
                }
            }
            stateChange();
            projectile.update();
        }
    }


    private void stateChange() {
        if (gameChangeKey()) {
            switch (keyH.isKey) {
                case A -> {
                    spriteNum = 0;
                    spriteCounter = 0;
                    stopCounter = 0;
                    tabAttack();
                    isAttacking = true;
                }
                case CONTROL -> speed = speed == 4 ? 8 : 4;
                case P -> {
                    gameState = pauseState;
                    gameMusic.stopMusic();
                }
                case Enter, Z -> {
                    interactNPC(cChecker.checkEntity(this, npc));
                    eHandler.checkEvent();
                }
                case X -> gameState = characterState;
                case S -> {
                    if (!projectile.alive){
                        projectile.set(worldX, worldY,direction);
                        projectile.alive=true;
                    }
                }
            }
        }
    }

    private void pickUpObject(int i) {
        if (i != 999) {//如果没有没碰到任何东西,也就是碰到了obj中的东西

            String text;

            if (inventoryBag.size() < maxBagSize) {
                inventoryBag.add(obj[currentMap][i]);
                gameMusic.playSE(1);
                text = "你获得了" + obj[currentMap][i].name;
            } else {
                text = "背包满了";
            }
            obj[currentMap][i] = null;
            ui.addMessage(text);
        }
    }

    private void interactNPC(int i) {
        if (i != 999) {
            gameState = dialogueState;
            npc[currentMap][i].speak();
        }
    }

    //碰到怪物
    private void contactMonster(int i) {
        if (i != 999) {
            if (!invincible && !monster[currentMap][i].dying) {
                gameMusic.playSE(6);
                invincible = true;
            }
        }
    }

    //玩家的攻击方法
    private void attacking() {
        spriteAttack++;
        if (spriteAttack <= 5) {
            spriteNum = 0;
        } else if (spriteAttack <= 25) {
            spriteNum = 1;
            if (spriteAttack == 20) {
                switch (direction) {
                    case upAttack -> {
                        attackArea.setX(worldX + solidAreaX + (solidAreaWidth - attackArea.getWidth()) / 2);
                        attackArea.setY(worldY + solidAreaY - attackArea.getHeight() - 8);
                    }
                    case downAttack -> {
                        attackArea.setX(worldX + solidAreaX + (solidAreaWidth - attackArea.getWidth()) / 2);
                        attackArea.setY(worldY + solidAreaY + solidAreaHeight);
                    }
                    case leftAttack -> {
                        attackArea.setX(worldX + solidAreaX - attackArea.getHeight());
                        attackArea.setY(worldY + solidAreaY + (solidAreaWidth - attackArea.getWidth()) / 2);
                    }
                    case rightAttack -> {
                        attackArea.setX(worldX + solidAreaX + solidAreaWidth);
                        attackArea.setY(worldY + solidAreaY + (solidAreaWidth - attackArea.getWidth()) / 2);
                    }
                }
                damageMonster(cChecker.attackEntity(attackArea.getBoundsInLocal(), monster), attack);
            }
        }//攻击动画播放完毕 复位
        else {
            switch (direction) {
                case upAttack -> direction = upDir;
                case downAttack -> direction = downDir;
                case leftAttack -> direction = leftDir;
                case rightAttack -> direction = rightDir;
            }
            spriteNum = 0;
            spriteAttack = 0;
            isAttacking = false;
        }
    }

    public void damageMonster(int i, int attack) {
        if (i != 999) {
            if (!monster[currentMap][i].invincible) {
                gameMusic.playSE(5);
                int damage = attack - monster[currentMap][i].defense;
                if (damage < 0) {
                    damage = 0;//最小伤害为0;
                }
                monster[currentMap][i].hp -= damage;
                monster[currentMap][i].invincible = true;
                monster[currentMap][i].damageReaction();

                if (monster[currentMap][i].hp <= 0) {
                    monster[currentMap][i].invincible = false;
                    monster[currentMap][i].dying = true;
                    ui.addMessage("你击杀了" + monster[currentMap][i].name + "\n获得" + monster[currentMap][i].exp + "Exp!");
                    exp += monster[currentMap][i].exp;
                    checkLevelUp();
                } else {
                    ui.addMessage("你造成了" + damage + "点伤害!");
                }
            }
        }
    }

    private void checkLevelUp() {
        if (exp >= nextLevelExp) {

            level++;
            nextLevelExp = nextLevelExp * 2;
            maxHp += 2;
            hp = maxHp;
            strength++;
            dexterity++;
            attack = getAttack();
            defense = getDefense();
            gameMusic.playSE(8);
            gameState = dialogueState;
            ui.currentDialogue = "你升到了" + level + "级\n力量增加,敏捷增加";
        }
    }


    //移动按键
    private boolean isMoveKey() {
        return keyH.isHeld(Key.UP) ||
                keyH.isHeld(Key.DOWN) ||
                keyH.isHeld(Key.LEFT) ||
                keyH.isHeld(Key.RIGHT);
    }

    private boolean gameChangeKey() {
        return keyH.isPressed(Key.CONTROL) ||
                keyH.isPressed(Key.A) ||
                keyH.isPressed(Key.S) ||
                keyH.isPressed(Key.Z) ||
                keyH.isPressed(Key.X) ||
                keyH.isPressed(Key.Enter) ||
                keyH.isPressed(Key.P);
    }

    public void tabAttack() {
        switch (direction) {
            case upDir -> direction = upAttack;
            case downDir -> direction = downAttack;
            case leftDir -> direction = leftAttack;
            case rightDir -> direction = rightAttack;
        }
    }

    //背包内切换装备,使用物品
    public void selectItem() {
        int bagIndexOnSlot = ui.getBagIndexOnSlot();
        if (bagIndexOnSlot < inventoryBag.size()) {
            Supplies selectedItem = inventoryBag.get(bagIndexOnSlot);
            switch (selectedItem.type) {
                case "attack", "axe" -> {
                    currentWeapon = selectedItem;
                    attack = getAttack();
                    readAttackImage("/boy/attack/boy_", selectedItem.type, "_%s_%d.png", tileSize, tileSize);
                }
                case "shield" -> {
                    currentShield = selectedItem;
                    defense = getDefense();
                }
                case "potion_red" -> {
                    selectedItem.use();
                    inventoryBag.remove(selectedItem);
                }
            }
        }
    }
}
